package cn.ibizlab.util.service;

import cn.ibizlab.util.domain.IEntity;
import cn.ibizlab.util.filter.ISearchContext;
import cn.ibizlab.util.filter.QueryFilter;
import cn.ibizlab.util.helper.BeanCache;
import cn.ibizlab.util.security.AuthenticationUser;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.util.ObjectUtils;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * spring security 权限管理类
 *
 * @author generator
 */
public class AbstractPermissionEvaluator implements PermissionEvaluator {

    @Value("${ibiz.enablePermissionValid:false}")
    boolean enablePermissionValid;  //是否开启权限校验

    public boolean isEnablePermissionValid() {
        return enablePermissionValid;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        return true;
    }

    /**
     * 服务接口鉴权
     * @param authentication 用户
     * @param targetDomainObject 实体
     * @param permission 操作符
     * @return
     */
    @Override
    @SneakyThrows
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        if(!isEnablePermissionValid())
            return true;
        Object principal = authentication.getPrincipal();
        if(ObjectUtils.isEmpty(principal))
            return false;
        AuthenticationUser authenticationUser = null;
        if(principal instanceof AuthenticationUser) {
            authenticationUser = (AuthenticationUser) authentication.getPrincipal();
            if (authenticationUser.isSuperUser())
                return true;
        }
        if(ObjectUtils.isEmpty(authentication.getAuthorities()))
            return false;

        String[] pairs = String.valueOf(permission).split("-|:");
        String entityTag = pairs.length>=2 ? pairs[1] : "";
        String action = pairs.length>=3 ? pairs[2] : "";

        Set<String> userAuthorities = authentication.getAuthorities().stream()
                .filter(item->item.getAuthority().startsWith((String)permission)).map(item->item.getAuthority()).collect(Collectors.toSet());
        if(userAuthorities.isEmpty())
            return false;
        if(userAuthorities.stream().filter(item->item.endsWith(String.format("%s-all",action))).findAny().isPresent())
            return true;
        if(targetDomainObject instanceof  ArrayList && !((ArrayList<?>) targetDomainObject).isEmpty() && ((ArrayList<?>) targetDomainObject).get(0) instanceof IEntity){
            for(IEntity entityBase: (List<IEntity>) targetDomainObject){
                if(!actionValid(entityBase, entityTag, action, (String)permission, userAuthorities, authenticationUser))
                    return false;
            }
        }
        else if (targetDomainObject instanceof ISearchContext)
            addFetchPermissionCondition((ISearchContext) targetDomainObject, entityTag, action, (String)permission, userAuthorities, authenticationUser);
        else if (targetDomainObject instanceof IEntity)
            return actionValid((IEntity) targetDomainObject, entityTag, action, (String)permission, userAuthorities,authenticationUser);
        return true;
    }

    public BeanCache.BeanSchema getSchema(String entityTag) {
        return BeanCache.get(entityTag);
    }


    /**
     * 实体查询数据权限条件附加
     * @param qc 接口过滤入参  附加条件在此参数中
     * @param entityTag 实体标记
     * @param action 查询方法名
     * @param permission 接口权限资源符
     * @param userAuthorities 相关权限资源
     * @param authenticationUser 当前操作者
     */
    public void addFetchPermissionCondition(ISearchContext qc, String entityTag, String action, String permission, Set<String> userAuthorities, AuthenticationUser authenticationUser){
        BeanCache.BeanSchema schema = getSchema(entityTag);
        if(schema == null)
            schema = getSchema(qc.getClass().getSimpleName());
        if(schema == null)
            return;
        String orgField=(schema.getOrgField()!=null)?schema.getOrgField().getFieldName():null;
        String orgDeptField=(schema.getOrgDeptField()!=null)?schema.getOrgDeptField().getFieldName():null;
        String orgTeamField=(schema.getOrgTeamField()!=null)?schema.getOrgTeamField().getFieldName():null;
        String createManField=(schema.getCreateManField()!=null)?schema.getCreateManField().getFieldName():null;

        Set<String> userOrg = new HashSet<>();
        Set<String> userOrgDept = new HashSet<>();
        Set<String> userTeam = new HashSet<>();
        Set<String> userCreateMan = new HashSet<>();
        for(String authority:userAuthorities) {
            if(authority.endsWith("curorg"))//本单位
                userOrg.addAll(authenticationUser.getCurOrgIds());
            else if(authority.endsWith("porg"))//上级单位
                userOrg.addAll(authenticationUser.getParentOrgIds());
            else if(authority.endsWith("sorg")) {//下级单位
                userOrg.addAll(authenticationUser.getCurOrgIds());
                userOrg.addAll(authenticationUser.getSubOrgIds());
            }
            else if(authority.endsWith("curorgdept"))//本部门
                userOrgDept.addAll(authenticationUser.getCurDeptIds());
            else if(authority.endsWith("porgdept"))//上级部门
                userOrgDept.addAll(authenticationUser.getParentDeptIds());
            else if(authority.endsWith("sorgdept")) {//下级部门
                userOrgDept.addAll(authenticationUser.getCurDeptIds());
                userOrgDept.addAll(authenticationUser.getSubDeptIds());
            }
            else if(authority.endsWith("curteam"))//本组
                userTeam.addAll(authenticationUser.getCurTeamIds());
            else if (authority.endsWith("createman"))
                userCreateMan.add(authority);
        }
        qc.setFilter(QueryFilter.createQuery());
        if(userOrg.size()==0 && userOrgDept.size()==0 && userCreateMan.size()==0)
            qc.getFilter().custom("1<>1");
        else {
            if((!ObjectUtils.isEmpty(orgField))&&userOrg.size()>0)
                qc.getFilter().or(QueryFilter.createQuery().in(orgField,userOrg));
            if((!ObjectUtils.isEmpty(orgDeptField))&&userOrgDept.size()>0)
                qc.getFilter().or(QueryFilter.createQuery().in(orgDeptField,userOrgDept));
            if((!ObjectUtils.isEmpty(orgTeamField))&&userTeam.size()>0)
                qc.getFilter().or(QueryFilter.createQuery().in(orgTeamField,userTeam));
            if((!ObjectUtils.isEmpty(createManField))&&userCreateMan.size()>0)
                qc.getFilter().or(QueryFilter.createQuery().eq(createManField,authenticationUser.getUserid()));
        }
    }

    /**
     * 实体行为权限校验
     * @param entity 接口入参
     * @param entityTag 实体标记
     * @param action 行为方法名
     * @param permission 接口权限资源符
     * @param userAuthorities 相关权限资源
     * @param authenticationUser 当前操作者
     * @return boolean
     */
    public boolean actionValid(IEntity entity, String entityTag, String action, String permission, Set<String> userAuthorities, AuthenticationUser authenticationUser){
        BeanCache.BeanSchema schema = getSchema(entityTag);
        if(schema == null)
            schema = getSchema(entity.getClass().getSimpleName());
        if(schema == null)
            return false;
        Object orgFieldValue=null;
        if(schema.getOrgField()!=null)
            orgFieldValue=entity.get(schema.getOrgField().getCodeName());
        Object orgDeptFieldValue=null;
        if(schema.getOrgDeptField()!=null)
            orgDeptFieldValue=entity.get(schema.getOrgDeptField().getCodeName());
        Object orgTeamFieldValue=null;
        if(schema.getOrgTeamField()!=null)
            orgTeamFieldValue=entity.get(schema.getOrgTeamField().getCodeName());
        Object crateManFieldValue=null;
        if(schema.getCreateManField()!=null)
            crateManFieldValue=entity.get(schema.getCreateManField().getCodeName());

        Set<String> userOrg = new HashSet<>();
        Set<String> userOrgDept = new HashSet<>();
        Set<String> userTeam = new HashSet<>();
        for(String authority:userAuthorities) {
            if(authority.endsWith("curorg"))//本单位
                userOrg.addAll(authenticationUser.getCurOrgIds());
            else if(authority.endsWith("porg"))//上级单位
                userOrg.addAll(authenticationUser.getParentOrgIds());
            else if(authority.endsWith("sorg")) {//下级单位
                userOrg.addAll(authenticationUser.getCurOrgIds());
                userOrg.addAll(authenticationUser.getSubOrgIds());
            }
            else if(authority.endsWith("curorgdept"))//本部门
                userOrgDept.addAll(authenticationUser.getCurDeptIds());
            else if(authority.endsWith("porgdept"))//上级部门
                userOrgDept.addAll(authenticationUser.getParentDeptIds());
            else if(authority.endsWith("sorgdept")) {//下级部门
                userOrgDept.addAll(authenticationUser.getCurDeptIds());
                userOrgDept.addAll(authenticationUser.getSubDeptIds());
            }
            else if(authority.endsWith("curteam"))//本组
                userTeam.addAll(authenticationUser.getCurTeamIds());
        }

        if(action.endsWith("Create") || action.endsWith("Save")) {
            if(!ObjectUtils.isEmpty(orgFieldValue) && !userOrg.contains(orgFieldValue))
                return false;
            if(!ObjectUtils.isEmpty(orgDeptFieldValue) && !userOrgDept.contains(orgDeptFieldValue))
                return false;
            if(!ObjectUtils.isEmpty(orgTeamFieldValue) && !userTeam.contains(orgTeamFieldValue))
                return false;
            if(!ObjectUtils.isEmpty(crateManFieldValue) && !authenticationUser.getUserid().equals(crateManFieldValue))
                return false;
            return true;
        }
        else {
            if(!ObjectUtils.isEmpty(orgFieldValue) && userOrg.contains(orgFieldValue))
                return true;
            if(!ObjectUtils.isEmpty(orgDeptFieldValue) && userOrgDept.contains(orgDeptFieldValue))
                return true;
            if(!ObjectUtils.isEmpty(orgTeamFieldValue) && userTeam.contains(orgTeamFieldValue))
                return true;
            return !ObjectUtils.isEmpty(crateManFieldValue) && authenticationUser.getUserid().equals(crateManFieldValue);
        }
    }

}